package com.tomorrow.runner.data;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.support.annotation.NonNull;

import cn.sucun.android.log.Log;
import cn.sucun.android.utils.JavaCalls;

/**
 * get authority from packageInfo
 * 
 * @author Jiangchangzheng <br>
 * 
 *         create at 2014-8-13 10:09:37
 * 
 */
abstract class AbsContentProvider extends ContentProvider {
	private static final String LOG_TAG = AbsContentProvider.class
			.getSimpleName();

	private static final String CALL_ARG = "call_arg";
	private static final String CALL_METHOD = "call_method";
	private static final String CALL_BUNDLE = "call_bundle";

	private static String sAuthority;

	@Override
	public void attachInfo(Context context, ProviderInfo info) {
		sAuthority = info.authority;
		super.attachInfo(context, info);
	}

	/**
	 * Init ContentProvider get Authority,Application will call this function to
	 * init app ContentProvider
	 * 
	 * @param c
	 *            application instance object
	 * @param clazz 类型
	 */
	public static void init(Context c, Class<? extends AbsContentProvider> clazz) {
		if (sAuthority == null) {
			ProviderInfo info = null;
			ProviderInfo[] providerInfos = null;
			try {
				providerInfos = c.getPackageManager().getPackageInfo(
						c.getPackageName(), PackageManager.GET_PROVIDERS).providers;
			} catch (NameNotFoundException e) {
				Log.w(LOG_TAG, "Failed get Package info.", e);
			}

			if (providerInfos != null) {
				final String className = clazz.getName();
				for (ProviderInfo providerInfo : providerInfos) {
					if (className.equals(providerInfo.name)) {
						info = providerInfo;
						break;
					}
				}
			}

			if (info == null) {
				throw new IllegalArgumentException(
						"Not found the definition for this Provider in AndroidManifest.xml.");
			}
			sAuthority = info.authority;
			
			Log.i(LOG_TAG,"sAuthority"+sAuthority);
		}
	}

	/**
	 * 
	 * @return authority
	 */
	public static String getAuthority() {
		if (sAuthority == null) {
			throw new RuntimeException(
					"The method must call after class instantiated by framework.");
		}
		return sAuthority;
	}

	@Override
	public final Uri insert(@NonNull Uri uri, ContentValues values) {
		if (isCall(uri)) {
			call(values.getAsString(CALL_METHOD), values.getAsString(CALL_ARG),
					byteToBundle(values.getAsByteArray(CALL_BUNDLE)));
			return null;
		} else {
			return insertRow(uri, values);
		}
	}

	/**
	 * RunProvider.call(getContentResolver(), RunProvider.getCallUri(),
	 * ScFileProvider.CALL_REFRESH_PATH, uri.toString(), null);
	 * 
	 * @param contentResolver
	 * @param uri
	 *            content://{authority}/call
	 * @param method
	 *            call function like CALL_REFRESH_PATH
	 * @param arg
	 * @param extras
	 * 
	 * @return
	 */
	public static Bundle call(ContentResolver contentResolver, Uri uri,
							  String method, String arg, Bundle extras) {

		Bundle result = null;
		boolean done = false;
		try {
			if (Build.VERSION.SDK_INT > 10) {
				result = JavaCalls.callMethodOrThrow(contentResolver, "call",
						uri, method, arg, extras);
				done = true;
			}
		} catch (Throwable t) {
			// XXX Wish the call method will not exception in self...
		}

		if (!done) {
			// this way not support return.
			contentResolver.insert(uri,
					transforeToContentValues(method, arg, extras));
		}
		return result;
	}

	private static ContentValues transforeToContentValues(String method,
														  String arg, Bundle extras) {

		ContentValues values = new ContentValues();
		values.put(CALL_METHOD, method);
		values.put(CALL_ARG, arg);

		if (extras != null) {
			values.put(CALL_BUNDLE, bundleToByte(extras));
		}

		return values;
	}

	private static byte[] bundleToByte(Bundle bundle) {
		if (bundle == null) {
			return null;
		}
		Parcel parcel = Parcel.obtain();
		try {
			bundle.writeToParcel(parcel, 0);
			return parcel.marshall();
		} finally {
			parcel.recycle();
		}
	}

	private static Bundle byteToBundle(byte[] data) {
		if (data == null) {
			return null;
		}
		Parcel parcel = Parcel.obtain();
		try {
			parcel.unmarshall(data, 0, data.length);
			parcel.setDataPosition(0);
			return Bundle.CREATOR.createFromParcel(parcel);
		} finally {
			parcel.recycle();
		}
	}

	protected boolean isCall(Uri uri) {
		return false;
	}

	abstract public Uri insertRow(Uri uri, ContentValues values);

	public Bundle call(@NonNull String method, String arg, Bundle extras) {
		return null;
	}
}
